package gr.aueb.mscis.chord;

import gr.aueb.mscis.configuration.Config;
import gr.aueb.mscis.protocol.NodeIdentifier;
import gr.aueb.mscis.protocol.ProtocolType;
import gr.aueb.mscis.protocol.RemoteMessage;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import android.util.Log;

public class RemoteMessageAgent {
	private ObjectOutputStream oos;
	private ObjectInputStream ois;

	public RemoteMessage register(NodeIdentifier node) {
		Socket s = null;
		RemoteMessage response = null;
		try {
			s = new Socket(Config.trackerAddr, Config.trackerPort);
		} catch (UnknownHostException e) {
			Log.e("register", e.getMessage());
		} catch (IOException e) {
			Log.e("register", e.getMessage());
		}
		try {
			// outgoing stream redirect to socket
			oos = new ObjectOutputStream(s.getOutputStream());
			/* mazi me to register stelnetai kai to port pou 8a akouei o kombos */
			oos.writeObject(new RemoteMessage(ProtocolType.REGISTER, null, node));

			ois = new ObjectInputStream(s.getInputStream());
			response = (RemoteMessage) ois.readObject();
			oos.close();
			ois.close();
			s.close();

		} catch (SocketException e) {
			Log.e("register", e.getMessage());
		} catch (UnknownHostException e) {
			Log.e("register", e.getMessage());
		} catch (IOException e) {
			Log.e("register", e.getMessage());
		} catch (ClassNotFoundException e) {
			Log.e("register", e.getMessage());
		}
		return response;
	}

	/**
	 * @param id
	 *            = hash tou opoiou psaxnoume ton successor
	 * @param niIntermediate
	 *            = to finger to opoio rwtaw san next hop
	 * @param niFinalDestination
	 *            = o komvos ston opoio tha stalei h telikh apanthsh
	 **/
	public void findSuccessor(String id, NodeIdentifier niIntermediate,
			NodeIdentifier niFinalDestination, int fingerPosition) {

		Socket s = null;
		int port = niIntermediate.getPort();
		InetAddress ipAddr = niIntermediate.getAddr();
		try {
			Log.v("ip:port", ipAddr + ":" + port);
			s = new Socket(ipAddr, port);
		} catch (SocketException e) {
			Log.e("findSuccessor", e.getMessage());
			return;
		} catch (UnknownHostException e) {
			Log.e("findSuccessor", e.getMessage());
			return;
		} catch (IOException e) {
			Log.e("findSuccessor", e.getMessage());
			return;
		}

		// outgoing stream redirect to socket
		try {
			oos = new ObjectOutputStream(s.getOutputStream());
			/* mazi me to register stelnetai kai to port pou 8a akouei o kombos */
			oos.writeObject(new RemoteMessage(ProtocolType.FIND_SUCCESSOR,
					null, niFinalDestination));
			oos.close();
			s.close();
		} catch (Exception e) {
		}

	}

	public void findSuccessorReply(NodeIdentifier successorId,
			NodeIdentifier destinationId, int fingerPosition) {
		Socket s = null;
		int port = destinationId.getPort();
		InetAddress ipAddr = destinationId.getAddr();
		try {
			
			Log.v("ip:port", ipAddr + ":" + port);
			s = new Socket(ipAddr, port);
			
		} catch (SocketException e) {
			Log.e("findSuccessorReply", e.getMessage());
			return;
		} catch (UnknownHostException e) {
			Log.e("findSuccessorReply", e.getMessage());
			return;
		} catch (IOException e) {
			Log.e("findSuccessorReply", e.getMessage());
			return;
		}

		// outgoing stream redirect to socket
		try {
			oos = new ObjectOutputStream(s.getOutputStream());
			oos.writeObject(new RemoteMessage(ProtocolType.FIND_SUCCESSOR_REPLY, null, successorId));
			
			oos.close();
			s.close();
		} catch (Exception e) {
		}
	}

	public void getSuccessors(Node node) {
		Node.successorList[0] = node.getSuccessor();
		Socket s = null;
		int port = node.getSuccessor().getPort();
		InetAddress ipAddr = node.getSuccessor().getAddr();
		try {
			Log.v("ip:port", ipAddr + ":" + port);
			s = new Socket(ipAddr, port);
		} catch (SocketException e) {
			Log.e("getSuccessors", e.getMessage());
			return; 
		} catch (UnknownHostException e) {
			Log.e("getSuccessors", e.getMessage());
			return;
		} catch (IOException e) {
			Log.e("getSuccessors", e.getMessage());
			return;
		}

		try {
			oos = new ObjectOutputStream(s.getOutputStream());
			oos.writeObject(new RemoteMessage(ProtocolType.GET_SUCCESSOR_LIST, null, node.getNodeId()));
			ois = new ObjectInputStream(s.getInputStream());
			RemoteMessage msg = (RemoteMessage)ois.readObject();
			for(int i=1; i<3; i++)
				Node.successorList[i] = ((NodeIdentifier[])msg.getPayload())[i-1];
			
			oos.close();
			ois.close();
			s.close();
		} catch (Exception e) {
		}
	}

	public NodeIdentifier getPredecessor(Node node) {
		Log.v("stabilize", "mphke sthn getPredecessor");
		Socket s = null;
		int port = node.getSuccessor().getPort();
		InetAddress ipAddr = node.getSuccessor().getAddr();
		try {
			s = new Socket(ipAddr, port);
		} catch (SocketException e) {
			Log.e("getPredecessor", e.getMessage());
			return null; 
		} catch (UnknownHostException e) {Log.e("getPredecessor", e.getMessage());
			return null;
		} catch (IOException e) {Log.e("getPredecessor", e.getMessage());
			return null;
		}

		try {
			
			oos = new ObjectOutputStream(s.getOutputStream());
			oos.writeObject(new RemoteMessage(ProtocolType.GET_PREDECESSOR, null, node.getNodeId()));
			ois = new ObjectInputStream(s.getInputStream());
			Log.v("stabilize", "esteile GET_PREDECESSOR");
			RemoteMessage msg = (RemoteMessage)ois.readObject();
			
			oos.close();
			ois.close();
			s.close();
			
			return msg.getNodeId();
					
		} catch (Exception e) {
		}
		return null;
	}

}
